package boardgamer.yavalath.client.lobby;

import android.app.Activity;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Rect;
import android.graphics.RectF;
import android.view.MotionEvent;
import boardgamer.common.client.Button;
import boardgamer.common.client.HasEventHandler;
import boardgamer.common.client.animation.Screen;
import boardgamer.common.client.layout.InvisibleRect;
import boardgamer.common.client.layout.LayoutGroup;
import boardgamer.yavalath.client.MainView;
import boardgamer.yavalath.client.GoButton;
import boardgamer.yavalath.client.Header;
import boardgamer.yavalath.client.Settings;
import boardgamer.yavalath.client.SharedObjects;
import boardgamer.yavalath.client.YavalathPlayer;
import boardgamer.yavalath.client.mainmenu.MenuPanel;
import boardgamer.yavalath.client.resources.SoundHandler;
import boardgamer.yavalath.common.YavalathPlayerState;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;

import java.util.Map;

import static com.google.common.base.Preconditions.checkNotNull;

/**
 * @author garysoed
 */
public class LocalLobbyScreen implements Screen {

  public static final int MAX_PLAYER_COUNT = 3;

  public static final float PLAYERS_TOP_RATIO = 0.3f;
  public static final float PLAYERS_GAP_RATIO = 0.05f;
  public static final float PLAYERS_WIDTH_RATIO = 0.9f;

  public static final float GO_TOP_RATIO = 0.7f;
  public static final float GO_HEIGHT_RATIO = 0.15f;

  private Header header;
  private LocalPlayerButton[] playerButtons;
  private MenuPanel menuPanel;
  private Button goButton;

  private final MainView gameView;
  private final Activity activity;

  public LocalLobbyScreen(MainView gameView, Activity activity) {
    this.gameView = checkNotNull(gameView, "view");
    this.activity = checkNotNull(activity, "activity");
  }

  @Override
  public void initialize(Rect canvasRect) {
    // initialize the header
    header = new Header("Local Game", canvasRect);

    // Initialize the players
    playerButtons = new LocalPlayerButton[MAX_PLAYER_COUNT];
    for (int playerId = 0; playerId < playerButtons.length; playerId++) {
      if (playerId == 2) {
        playerButtons[playerId] = new LocalPlayerButton(
            playerId, new PlayerColorPredicate(playerId), new PlayerModePredicate(),
            YavalathPlayerState.PlayerMode.CLOSED  /* isEditable */, activity);
      } else {
        playerButtons[playerId] = new LocalPlayerButton(
            playerId,
            new PlayerColorPredicate(playerId),
            new PlayerModePredicate(),
            activity); // isEditable
      }
    }
    
    LayoutGroup playerButtonGroup = new LayoutGroup();
    for (int playerId = 0; playerId < playerButtons.length; playerId++) {
      if (playerId <= 0) {
        playerButtonGroup.add(playerButtons[playerId]).atCurrentRect();
      } else {
        playerButtonGroup.add(playerButtons[playerId])
            .relativeToEdge(
                playerButtons[playerId - 1],
                LayoutGroup.Direction.BOTTOM,
                canvasRect.height() * PLAYERS_GAP_RATIO);
      }
    }

    playerButtonGroup.setBoundingRect(new RectF(
        0,
        canvasRect.height() * PLAYERS_TOP_RATIO,
        canvasRect.width() * PLAYERS_WIDTH_RATIO,
        canvasRect.height()),
        LayoutGroup.VerticalAlign.TOP,
        LayoutGroup.HorizontalAlign.LEFT);

    // Create Go button
    goButton = new GoButton(new HasEventHandler.EventHandler() {
      @Override
      public void onEvent(float eventX, float eventY, int eventAction) {
        if (eventAction == MotionEvent.ACTION_UP) {
          SoundHandler.playWave(SoundHandler.buttonSound);
          gameView.startNewGame(getPlayers());
        }
      }
    });
    float goTop = canvasRect.height() * GO_TOP_RATIO;
    LayoutGroup goGroup = new LayoutGroup()
        .add(goButton).atCurrentRect();
    goGroup.setBoundingRect(new RectF(
        0,
        goTop,
        canvasRect.width(),
        goTop + canvasRect.height() * GO_HEIGHT_RATIO
    ));
    goGroup.add(new InvisibleRect(canvasRect)).atCurrentRect()
        .horizontalAlign(LayoutGroup.HorizontalAlign.CENTER);

    menuPanel = new MenuPanel(Color.RED, canvasRect);
  }

  @Override
  public void onOpen() { }

  @Override
  public void onResume() {
    refreshPlayerNames();
  }

  public Map<YavalathPlayer, YavalathPlayerState> getPlayers() {
    Map<YavalathPlayer, YavalathPlayerState> players = Maps.newLinkedHashMap();
    for (LocalPlayerButton playerButton : playerButtons) {
      if (playerButton.getPlayerMode() != YavalathPlayerState.PlayerMode.CLOSED) {
        players.put(playerButton.getPlayer(), playerButton.getPlayerState());
      }
    }
    return ImmutableMap.copyOf(players);
  }

  @Override
  public void onUpdate(int elapsedTime) { }

  @Override
  public void onDraw(Canvas canvas) {
    header.draw(canvas);
    for (LocalPlayerButton playerButton : playerButtons) {
      playerButton.draw(canvas);
    }
    menuPanel.draw(canvas);
    goButton.draw(canvas);
  }

  @Override
  public void onTouchEvent(MotionEvent event) {
    for (LocalPlayerButton playerButton : playerButtons) {
      playerButton.handleEvent(event.getX(), event.getY(), event.getAction());
    }
    menuPanel.handleEvent(event.getX(), event.getY(), event.getAction());
    goButton.handleEvent(event.getX(), event.getY(), event.getAction());
  }

  private boolean canSetPlayerColor(int playerId, YavalathPlayerState.PlayerColor playerColor) {
    // Check if other players have the queried playerColor
    for (int i = 0; i < MAX_PLAYER_COUNT; i++) {
      if (i != playerId && Settings.instance().getPlayer(i).getColor() == playerColor) {
        return false;
      }
    }
    return true;
  }

  public void refreshPlayerNames() {
    for (LocalPlayerButton playerButton : playerButtons) {
      playerButton.refreshPlayerName();
    }
  }

  private boolean canSetPlayerMode(YavalathPlayerState.PlayerMode playerMode) {
    switch (playerMode) {
      case CLOSED:
        // Make sure that at least 2 players are in game
        Iterable activePlayers = Iterables.filter(ImmutableSet.copyOf(playerButtons),
            new Predicate<LocalPlayerButton>() {
              @Override
              public boolean apply(LocalPlayerButton input) {
                return input.getPlayerMode() != YavalathPlayerState.PlayerMode.CLOSED;
              }
            });
        return Iterables.size(activePlayers) > 2;

      case AI:
        return SharedObjects.instance().getProFeatures().isEnabled();

      default:
        return true;
    }
  }

  private class PlayerColorPredicate implements Predicate<YavalathPlayerState.PlayerColor> {

    private final int playerId;

    public PlayerColorPredicate(int playerId) {
      this.playerId = playerId;
    }

    @Override
    public boolean apply(YavalathPlayerState.PlayerColor playerColor) {
      return canSetPlayerColor(playerId, playerColor);
    }
  }

  private class PlayerModePredicate implements Predicate<YavalathPlayerState.PlayerMode> {

    @Override
    public boolean apply(YavalathPlayerState.PlayerMode playerMode) {
      return canSetPlayerMode(playerMode);
    }
  }
}
